package com.asa.renderer;

import org.joml.*;
import org.lwjgl.BufferUtils;

import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;

import static org.lwjgl.opengl.GL11.GL_FALSE;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL20.glGetShaderInfoLog;

public class Shader {

    private int shaderProgramID;

    private boolean beingUsed = false;

    private String vertexSource;
    private String fragmentSource;

    private String filepath;

    public Shader(String filepath){
        this.filepath = filepath;

        try{
            String source = new String(Files.readAllBytes(Paths.get(filepath)));
            String[] splitString = source.split("(#type)( )+([a-zA-Z]+)");

            //找到第一个模式
            int index = source.indexOf("#type") + 6;
            int eol = source.indexOf("\r\n",index);
            String firstPattern = source.substring(index,eol).trim();

            //找到第二个模式
            index = source.indexOf("#type",eol) + 6;
            eol = source.indexOf("\r\n",index);
            String secondPattern = source.substring(index,eol).trim();

            if (firstPattern.equals("vertex")){
                vertexSource = splitString[1];
            } else if (firstPattern.equals("fragment")) {
                fragmentSource = splitString[1];
            }else {
                throw new IOException("Unexpcted token '" + firstPattern+ "'");
            }

            if (secondPattern.equals("vertex")){
                vertexSource = splitString[2];
            } else if (secondPattern.equals("fragment")) {
                fragmentSource = splitString[2];
            }else {
                throw new IOException("Unexpcted token '" + secondPattern+ "'");
            }

        }catch (IOException e){
            e.printStackTrace();
            assert false :"无法打开着色器文件";
        }

        System.out.println(vertexSource);
        System.out.println("---------------------");
        System.out.println(fragmentSource);

    }


    public void compile(){
        int vertexID,fragmentID;

        //加载并编译顶点着色器
        vertexID = glCreateShader(GL_VERTEX_SHADER);

        //将着色器代码传给GPU
        glShaderSource(vertexID,vertexSource);
        glCompileShader(vertexID);

        //检查编译过程中的错误
        int success = glGetShaderi(vertexID,GL_COMPILE_STATUS);

        if (success == GL_FALSE){
            int len = glGetShaderi(vertexID,GL_INFO_LOG_LENGTH);
            System.out.println("错误："+filepath+"\n顶点着色器编译失败");
            System.out.println(glGetShaderInfoLog(vertexID,len));
            assert false:"";
        }


        //加载并编译片段着色器
        fragmentID = glCreateShader(GL_FRAGMENT_SHADER);

        //将着色器代码传给GPU
        glShaderSource(fragmentID,fragmentSource);
        glCompileShader(fragmentID);

        //检查编译过程中的错误
        success = glGetShaderi(fragmentID,GL_COMPILE_STATUS);

        if (success == GL_FALSE){
            int len = glGetShaderi(fragmentID,GL_INFO_LOG_LENGTH);
            System.out.println("错误："+filepath+"\n片段着色器编译失败");
            System.out.println(glGetShaderInfoLog(fragmentID,len));
            assert false:"";
        }
        //链接着色器并检查错误

        shaderProgramID = glCreateProgram();
        glAttachShader(shaderProgramID,vertexID);
        glAttachShader(shaderProgramID,fragmentID);
        glLinkProgram(shaderProgramID);

        //检查连接错误
        success = glGetProgrami(shaderProgramID,GL_LINK_STATUS);
        if (success == GL_FALSE){
            int len = glGetProgrami(shaderProgramID,GL_INFO_LOG_LENGTH);
            System.out.println("错误："+filepath+"\n着色器链接失败");
            System.out.println(glGetProgramInfoLog(shaderProgramID,len));
            assert false:"";
        }

    }

    public void use(){
        if (!beingUsed){
            //绑定程序
            glUseProgram(shaderProgramID);
            beingUsed = true;
        }


    }


    public void detach(){
        glUseProgram(0);
        beingUsed = false;
    }


    public void uploadMat4f(String varName, Matrix4f mat4){

        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        FloatBuffer matBuffer = BufferUtils.createFloatBuffer(16);
        mat4.get(matBuffer);
        glUniformMatrix4fv(varLocation,false,matBuffer);


    }

    public void uploadMat4f(String varName, Matrix3f mat3){

        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        FloatBuffer matBuffer = BufferUtils.createFloatBuffer(9);
        mat3.get(matBuffer);
        glUniformMatrix3fv(varLocation,false,matBuffer);


    }





    public void uploadVec4f(String varName, Vector4f vec){

        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        glUniform4f(varLocation,vec.x,vec.y,vec.z,vec.w);

    }

    public void uploadVec3f(String varName, Vector3f vec){

        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        glUniform3f(varLocation,vec.x,vec.y,vec.z);

    }

    public void uploadVec2f(String varName, Vector2f vec){

        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        glUniform2f(varLocation,vec.x,vec.y);

    }

    public void uploadFloat(String varName, float val) {
        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        glUniform1f(varLocation,val);


    }

    public void uploadInt(String varName, int val) {
        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        glUniform1i(varLocation,val);

    }


    public void uploadTexture(String varName, int slot){
        int varLocation = glGetUniformLocation(shaderProgramID,varName);
        use();
        glUniform1i(varLocation,slot);

    }



}
